package 反射.代理;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import lombok.Data;
import lombok.ToString;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;
import net.sf.cglib.proxy.InterfaceMaker;
import net.sf.cglib.proxy.LazyLoader;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Dispatcher;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;

/*

Eclipse使用：在导入cglib包后，运行动态代理程序，出现错误：
Exception in thread "main" java.lang.ExceptionInInitializerError
	at 反射.动态代理ccglib.main(动态代理ccglib.java:21)
Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @4534b60d

解决方法：(eclipse)
1.选择【已安装JRE】-->【JDE-20.0.2】-->【运行方式】-->【运行配置】-->【VM自变量】，填入：--add-opens java.base/java.lang=ALL-UNNAMED


Cglib代理：Code Generation Library
原理：动态生成一个要代理类的子类(被代理的类作为继承的父类)，子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用。

JDK动态代理虽然简单易用，但是其有一个致命缺陷是，只能对接口进行代理。如果要代理的类为一个普通类、没有接口，那么Java动态代理就没法使用了。
JDK静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式
类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

 •JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
 •Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
 •Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

CGLIB的API
1、Jar包：
 •cglib-nodep-3.3.0.jar：使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
 •cglib-3.3.0.jar：使用此jar包需要关联asm的jar包,否则运行时报错.还需引入包：asm-9.5.jar

Maven依赖：
 <dependency>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>9.5</version>
</dependency>

2、CGLIB类库：
 •net.sf.cglib.core: 底层字节码处理类，他们大部分与ASM有关系。
 •net.sf.cglib.transform: 编译期或运行期类和类文件的转换
 •net.sf.cglib.proxy: 实现创建代理和方法拦截器的类
 •net.sf.cglib.reflect: 实现快速反射和C#风格代理的类
 •net.sf.cglib.util: 集合排序等工具类
 •net.sf.cglib.beans: JavaBean相关的工具类

Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的，但是，JDK中所要进行动态代理的类必须要实现一个接口，
也就是说只能对该类所实现接口中定义的方法进行代理，这在实际编程中具有一定的局限性，而且使用反射的效率也并不是很高。

使用CGLib实现动态代理，完全不受代理类必须实现接口的限制，而且CGLib底层采用ASM字节码生成框架，使用字节码技术生成代理类，比使用Java反射效率要高。
唯一需要注意的是，CGLib不能对声明为final的方法进行代理，因为CGLib原理是动态生成被代理类的子类。

通过一个实例介绍使用CGLib实现动态代理。

//1、被代理类(目标对象)：

//首先，定义一个类，该类没有实现任何接口。
*/
class TargetObject {//没有实现接口，需要CGlib动态代理的目标类
    public String method1(String paramName) {
        return paramName;
    }
    public int method2(int count) {
        return count;
    }
    public int method3(int count) {
        return count;
    }
    @Override
    public String toString() {
        return "TargetObject []"+ getClass();
    }
}

//2、拦截器：
//定义一个拦截器。在调用目标方法时，CGLib会回调MethodInterceptor接口方法拦截，来实现你自己的代理逻辑，类似于JDK中的InvocationHandler接口。

class TargetInterceptor implements MethodInterceptor{//目标对象拦截器，实现MethodInterceptor
   //* 重写方法拦截在方法前和方法后加入业务
   //* Object obj为目标对象,为由CGLib动态生成的代理类实例
   //* Method method为目标方法,Method为上文中实体类所调用的被代理的方法引用
   //* Object[] params 为参数，是参数值列表
   //* MethodProxy proxy CGlib方法代理对象,是生成的代理类对方法的代理引用。
   @Override
   public Object intercept(Object obj, Method method, Object[] params,MethodProxy proxy) throws Throwable {
      System.out.println("调用前");
      Object result = proxy.invokeSuper(obj, params);//调用代理类实例上的proxy方法的父类方法（即实体类TargetObject中对应的方法）
      System.out.println("执行结果： "+result+" "+method.getName());
      System.out.println("调用后");
      return result;//从代理实例的方法调用返回的值，在这个示例中，只在调用被代理类方法前后各打印了一句话，当然实际编程中可以是其它复杂逻辑。
  }
}
//3、生成动态代理类：

public class CGLIB动态代理 {
	public static void main(String args[]) {
		Enhancer enhancer = new Enhancer();//Enhancer类是CGLib中的一个字节码增强器，它可以方便的对你想要处理的类进行扩展，以后会经常看到它。
		enhancer.setSuperclass(TargetObject.class);//将被代理类TargetObject设置成父类
		enhancer.setCallback(new TargetInterceptor());//然后设置拦截器TargetInterceptor
		TargetObject targetObject2 = (TargetObject) enhancer.create();//执行enhancer.create()动态生成一个代理类
		System.out.println("1 "+targetObject2);//在代理类上调用方法
//		System.out.println(targetObject2.method1("mmm1"));//在代理类上调用方法
//		System.out.println(targetObject2.method2(100));//在代理类上调用方法
//		System.out.println(targetObject2.method3(200));//在代理类上调用方法
	}
}

//4、回调过滤器CallbackFilter
//一、作用
//设置多个拦截器，可通过过滤器(CallbackFilter)返回的多个拦截器的数组下标值(int)，确定使用哪个拦截器。
//在CGLib回调时可以设置对不同方法执行不同的回调逻辑，或者根本不执行回调。
//在JDK动态代理中并没有类似的功能，对InvocationHandler接口方法的调用对代理类内的所以方法都有效。
//定义实现过滤器CallbackFilter接口的类：
/*
class TargetMethodCallbackFilter implements CallbackFilter {// 回调方法过滤

	@Override
	public int accept(Method method) {// 过滤方法,返回的值为数字，代表了Callback数组中的索引位置，要到用的Callback
		if (method.getName().equals("method1")) {
			System.out.println("filter method1 ==0");
			return 0;
		}
		if (method.getName().equals("method2")) {
			System.out.println("filter method2 ==1");
			return 1;
		}
		if (method.getName().equals("method3")) {
			System.out.println("filter method3 ==2");
			return 2;
		}
		return 0;
	}
}
//其中return值为被代理类的各个方法在回调数组Callback[]中的位置索引（见下文）。
public class CGLIB动态代理 {
	public static void main(String args[]) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(TargetObject.class);
		CallbackFilter callbackFilter = new TargetMethodCallbackFilter();
		// * (1)callback1：方法拦截器 
		// * (2)NoOp.INSTANCE：这个NoOp表示no operator，即什么操作也不做，代理类直接调用被代理的方法不进行拦截。
		// * (3)FixedValue：表示锁定方法返回值，无论被代理类的方法返回什么值，回调方法都返回固定值。
		Callback noopCb = NoOp.INSTANCE;
		Callback callback1 = new TargetInterceptor();
		Callback fixedValue = new TargetResultFixed();
		Callback[] cbarray = new Callback[] { callback1,noopCb,fixedValue };
//		enhancer.setCallback(new TargetInterceptor());
		enhancer.setCallbacks(cbarray);
		enhancer.setCallbackFilter(callbackFilter);
		TargetObject targetObject2 = (TargetObject) enhancer.create();
		System.out.println(targetObject2);
		System.out.println(targetObject2.method1("mmm1"));
		System.out.println(targetObject2.method2(100));
		System.out.println(targetObject2.method3(100));
		System.out.println(targetObject2.method3(200));
	}
}

class TargetResultFixed implements FixedValue {// 表示锁定方法返回值，无论被代理类的方法返回什么值，回调方法都返回固定值。
	//该类实现FixedValue接口，同时锁定回调值为999
	//(整型，CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue，该方法返回值为整型)。
	@Override
	public Object loadObject() throws Exception {
		System.out.println("锁定结果");
		Object obj = 999;
		return obj;
	}
}
*/
/*
5.延迟加载对象
一、作用：
LazyLoader接口继承了Callback，因此也算是CGLib中的一种Callback类型。
另一种延迟加载接口Dispatcher。Dispatcher接口同样继承于Callback，也是一种回调类型。
但是Dispatcher和LazyLoader的区别在于：
LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法，而Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法。

1、设计一个延迟加载器（回调），实现CGLIB的LazyLoader接口，接口中有一个loadObject方法来实际加载对象。
2、在第一次创建域对象DOM(PO)的时候，使用CGLIB的Enhancer的create方法创建一个需要延迟加载的属性对象，这其中需要使用上面的延迟加载器作为参数。
3、当用户在第一次访问到延迟加载的对象时，则会调用延迟加载器中的loadObject方法来实际加载对象。

• LazyLoader接口:
public interface LazyLoader extends Callback {
    Object loadObject() throws Exception;
}
LazyLoader接口继承了Callback，因此也算是CGLib中的一种Callback类型。只在第一次访问延迟加载属性时触发代理类回调方法

• Dispatcher接口：
public interface Dispatcher extends Callback {
    Object loadObject() throws Exception;
}
Dispatcher接口同样继承于Callback，也是一种回调类型。每次访问延迟加载属性时都会触发代理类回调方法。

二、示例：
首先定义一个实体类LoaderBean，该Bean内有一个需要延迟加载的属性PropertyBean。
*/
/*
@Data
@ToString
class LazyBean {
	private String name;
	private int age;
	private PropertyBean propertyBean;
	private PropertyBean propertyBeanDispatcher;

	public LazyBean(String name, int age) {
      System.out.println("lazy bean init");
      this.name = name;
      this.age = age;
      this.propertyBean = createPropertyBean();//需要延迟加载的属性，只第一次懒加载
      this.propertyBeanDispatcher = createPropertyBeanDispatcher();//需要延迟加载的属性，每次都懒加载
  }

  private PropertyBean createPropertyBean() {//只第一次懒加载
       //* 使用cglib进行懒加载 对需要延迟加载的对象添加代理，在获取该对象属性时先通过代理类回调方法进行对象初始化。
       //* 在不需要加载该对象时，只要不去获取该对象内属性，该对象就不会被初始化了（在CGLib的实现中只要去访问该对象内属性的getter方法，
       //* 就会自动触发代理类回调）。
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(PropertyBean.class);
      PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());
      return pb;
  }

  private PropertyBean createPropertyBeanDispatcher() {//每次都懒加载
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(PropertyBean.class);
      PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class,new ConcreteClassDispatcher());
      return pb;
  }
}
@Data
@ToString
class PropertyBean {
	private String key;
	private Object value;
}

class ConcreteClassLazyLoader implements LazyLoader {
	 //* 对需要延迟加载的对象添加代理，在获取该对象属性时先通过代理类回调方法进行对象初始化。
	 //* 在不需要加载该对象时，只要不去获取该对象内属性，该对象就不会被初始化了（在CGLib的实现中只要去访问该对象内属性的getter方法，
	 //* 就会自动触发代理类回调）。 在LazyBean的构造方法中，对属性Bean进行了代理类生成，使用了CGLib中的LazyLoader回调接口。
	@Override
	public Object loadObject() throws Exception {
		System.out.println("before lazyLoader...");
		PropertyBean propertyBean = new PropertyBean();
		propertyBean.setKey("LazyLoader");
		propertyBean.setValue(new TargetObject());
		System.out.println("after lazyLoader...");
		return propertyBean;
	}

}

class ConcreteClassDispatcher implements Dispatcher {

	@Override
	public Object loadObject() throws Exception {
		System.out.println("before Dispatcher...");
		PropertyBean propertyBean = new PropertyBean();
		propertyBean.setKey("Dispatcher");
		propertyBean.setValue(new TargetObject());
		System.out.println("after Dispatcher...");
		return propertyBean;
	}

}
public class CGLIB动态代理{
	public static void main(String[] args) {
		LazyBean loader=new LazyBean("loadName",123);
		System.out.println(loader.getName());  
		System.out.println(loader.getAge());  
		PropertyBean propertyBean=loader.getPropertyBean();//访问延迟加载对象
		String str=propertyBean.getKey();//只要执行getKey(),同样触发回调
//		System.out.println("1"+propertyBean.getKey()); //触发回调 
//		System.out.println("1"+propertyBean.getValue());  
		//System.out.println("after...");  
		//当再次访问延迟加载对象时,就不会再执行回调了  
		System.out.println("------------------------------------------"); 
		System.out.println("2"+propertyBean.getKey());//不再回调
		System.out.println("2"+propertyBean.getValue()); //不再回调
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 
		
		PropertyBean propertyBeanDispatcher=loader.getPropertyBeanDispatcher();
		System.out.println("1"+propertyBeanDispatcher.getKey());  
		System.out.println("1"+propertyBeanDispatcher.getValue()); 
		System.out.println("=========================================="); 
		System.out.println("2"+propertyBeanDispatcher.getKey()); //再次回调
		System.out.println("2"+propertyBeanDispatcher.getValue()); //再次回调
	}
}
*/
/*
注意，第一次获取property bean的属性时，会触发代理类回调方法。第二次再获取property bean的属性时，就直接返回属性值而不会再次触发代理类回调方法了。
可见，延迟加载原理：
对需要延迟加载的对象添加代理，在获取该对象属性时先通过代理类回调方法进行对象初始化。
在不需要加载该对象时，只要不去获取该对象内属性，该对象就不会被初始化了（在CGLib的实现中只要去访问该对象内属性的getter方法，就会自动触发代理类回调）。
 */
/*
6.接口生成器InterfaceMaker
一、作用：
InterfaceMaker会动态生成一个接口，该接口包含指定类定义的所有方法。
二、示例：
*/
/*
public class CGLIB动态代理 {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {
		InterfaceMaker interfaceMaker = new InterfaceMaker();
		// 抽取某个类的方法生成接口方法
		interfaceMaker.add(TargetObject.class);
		Class<?> targetInterface = interfaceMaker.create();
		for (Method method : targetInterface.getMethods()) {
			System.out.println(method.getName());
		}
		// 接口代理并设置代理接口方法拦截
		//public static Object create(Class superclass, Class interfaces[], Callback callback)
		Object object = Enhancer.create(Object.class, new Class[] { targetInterface }, new MethodInterceptor() {
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				if (method.getName().equals("method1")) {
					System.out.println("filter method1 ");
					return "mmmmmmmmm";
				}
				if (method.getName().equals("method2")) {
					System.out.println("filter method2 ");
					return 1111111;
				}
				if (method.getName().equals("method3")) {
					System.out.println("filter method3 ");
					return 3333;
				}
				return "default";
			}
		});
		Method targetMethod1 = object.getClass().getMethod("method3", new Class[] { int.class });
		int i = (int) targetMethod1.invoke(object, new Object[] { 33 });
		Method targetMethod = object.getClass().getMethod("method1", new Class[] { String.class });
		System.out.println(targetMethod.invoke(object, new Object[] { "sdfs" }));
	}
}
*/
/*

public class CGLIB动态代理 {
	public void test() {
		System.out.println("Hello World !");
	}
	public static void main(String[] args) {
		Enhancer enhancer=new Enhancer();
		enhancer.setSuperclass(CGLIB动态代理.class);
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object obj,java.lang.reflect.Method method,Object[] args,MethodProxy proxy) throws Throwable {
				System.out.println("before method run...");
				Object result=proxy.invokeSuper(obj,args);
				System.out.println("after method run...");
				return result;
			}
		});
		CGLIB动态代理 sample=(CGLIB动态代理) enhancer.create();
		sample.test();
	}
}
*/


//CGLIB常用API介绍
//下面介绍一下CGLIB中常用的API，先建立一个模特接口类和普通模特类：
/*
class SampleClass {
	public String sayHello(String name) {
		return String.format("%s say hello!", name);
	}
}

interface SampleInterface {
	String sayHello(String name);
}*/
/*
【Enhancer:】
Enhancer，即(字节码)增强器。它是CGLIB库中最常用的一个类，功能JDK动态代理中引入的Proxy类差不多，但是Enhancer既能够代理普通的Java类，
也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用（包括从Object中继承的toString和hashCode方法）。Enhancer不能
够拦截final方法，例如Object.getClass()方法，这是由于final关键字的语义决定的。基于同样的道理，Enhancer也不能对fianl类进行代理操作。
这也是Hibernate为什么不能持久化final关键字修饰的类的原因。
使用CGLIB中的Enhancer来创建动态代理。
Enhancer是一个CGLIB中非常重要的类，它允许为非接口类型创建一个JAVA代理，Enhancer动态的创建给定类的子类，和JDK动态代理不一样的是不管代理的类是实现接口还
是普通的类它都能正常工作。然后通过 回调 来进行拦截代理类的方法进行处理。
*/
/*
public class CGLIB动态代理 {
	public static void main(String[] args) throws Exception {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(SampleClass.class);
        //使用FixedValue，拦截返回值，每次返回固定值"Doge say hello!"
		enhancer.setCallback((FixedValue) () -> "Doge say hello!");
		SampleClass sampleClass = (SampleClass) enhancer.create();
		System.out.println(sampleClass.sayHello("throwable-10086"));
		System.out.println(sampleClass.sayHello("throwable-doge"));
		System.out.println(sampleClass.toString());
		System.out.println(sampleClass.getClass());
		//System.out.println(sampleClass.hashCode());
	}
}

*/
//上述代码中，FixedValue用来对所有拦截的方法返回相同的值，从输出我们可以看出来，Enhancer对非final方法test()、toString()、hashCode()进行了拦截，
//没有对getClass进行拦截。由于hashCode()方法需要返回一个Number，但是我们返回的是一个String，这解释了上面的程序中为什么会抛出异常。
//
//Enhancer3setSuperclass()用来设置父类型，从toString()方法可以看出，使用CGLIB生成的类为被代理类的一个子类，类简写名称
//为SampleClass$$EnhancerByCGLIB$$e3ea9b7。
//
//Enhancer#create(Class[] argumentTypes, Object[] arguments)方法是用来创建增强对象的，其提供了很多不同参数的方法用来匹配被增强类的不同构造方
//法。我们也可以先使用Enhancer#createClass()来创建字节码(.class)，然后用字节码加载完成后的类动态生成增强后的对象。Enhancer中还有其他几个方法名
//为create的方法，提供不同的参数选择，具体可以自行查阅。
//
//下面再举个例子说明一下使用Enhancer代理接口：
//
//public class EnhancerInterfaceDemo {
//
//	public static void main(String[] args) throws Exception {
//		Enhancer enhancer = new Enhancer();
//		enhancer.setInterfaces(new Class[]{SampleInterface.class});
//		enhancer.setCallback((FixedValue) () -> "Doge say hello!");
//		SampleInterface sampleInterface = (SampleInterface) enhancer.create();
//		System.out.println(sampleInterface.sayHello("throwable-10086"));
//		System.out.println(sampleInterface.sayHello("throwable-doge"));
//		System.out.println(sampleInterface.toString());
//		System.out.println(sampleInterface.getClass());
//		System.out.println(sampleInterface.hashCode());
//	}
//}
//输出结果和上一个例子一致。
/*
【Callback】
Callback，即回调。值得注意的是，它是一个标识接口(空接口，没有任何方法)，它的回调时机是生成的代理类的方法被调用的时候。也就是说，生成的代理类的方法被调用的
时候，Callback的实现逻辑就会被调用。Enhancer通过setCallback()和setCallbacks()设置Callback，设置了多个Callback实例将会按照设置的顺序进行回
调。CGLIB中提供的Callback的子类有以下几种：

 •NoOp
 •FixedValue
 •InvocationHandler
 •MethodInterceptor
 •Dispatcher
 •LazyLoader

【NoOp】
NoOp，No Operation，也就是不做任何操作。这个回调实现只是简单地把方法调用委托给了被代理类的原方法(也就是调用原始类的原始方法)，不做任何其它的操作，所以不能
使用在接口代理。
*/
/*
public class NoOpDemo {
	public static void main(String[] args) throws Exception{
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(SampleClass.class);
		enhancer.setCallback(NoOp.INSTANCE);
		SampleClass sampleClass = (SampleClass) enhancer.create();
		System.out.println(sampleClass.sayHello("throwable"));
	}
}
*/
//输出结果：
//throwable say hello!
/*
【FixedValue】
FixedValue，Fixed Value，即固定值。它提供了一个loadObject()方法，不过这个方法返回的不是代理对象，而是原方法调用想要的结果。也就是说，在这个Callback里
面，看不到任何原方法的信息，也就没有调用原方法的逻辑，不管原方法是什么都只会调用loadObject()并返回一个固定结果。需要注意的是，如果loadObject()方法的返回值
并不能转换成原方法的返回值类型，那么会抛出类型转换异常(ClassCastException)。

最前面的Enhancer两个例子就是用FixedValue做分析的，这里不再举例。

【InvocationHandler】
InvocationHandler全类名为net.sf.cglib.proxy.InvocationHandler，它的功能和JDK动态代理中的java.lang.reflect.InvocationHandler类似，提供
了一个Object invoke(Object proxy, Method method, Object[] objects)方法。需要注意的是：所有对invoke方法的参数proxy对象的方法调用都会被委托给
同一个InvocationHandler，所以可能会导致无限循环(因为invoke中调用的任何原代理类方法，均会重新代理到invoke方法中)。举个简单的例子：
*/
/*
public class InvocationHandlerDeadLoopDemo {
	public static void main(String[] args) throws Exception{
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(SampleClass.class);
		enhancer.setCallback(new InvocationHandler() {
			@Override
			public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
				return method.invoke(o, objects);
			}
		});
		SampleClass sampleClass = (SampleClass) enhancer.create();
		System.out.println(sampleClass.sayHello("throwable"));
	}
}
*/
//上面的main方法执行后会直接爆栈，因为method#invoke()方法会重新调用InvocationHandler的invoke方法，形成死循环。正确的使用例子如下：
/*
public class InvocationHandlerDemo {
	public static void main(String[] args) throws Exception {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(SampleClass.class);
		enhancer.setCallback(new InvocationHandler() {
			@Override
			public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
				if (!Objects.equals(method.getDeclaringClass(), Object.class) && Objects.equals(String.class, method.getReturnType())) {
					return String.format("%s say hello!", objects);
				}
				return "No one say hello!";
			}
		});
		SampleClass sampleClass = (SampleClass) enhancer.create();
		System.out.println(sampleClass.sayHello("throwable"));
	}
}
*/
//输出结果：throwable say hello!
/*
【MethodInterceptor】
MethodInterceptor，即方法拦截器，这是一个功能很强大的接口，它可以实现类似于AOP编程中的环绕增强（Around Advice）。它只有一个方
法public Object intercept(Object obj,java.lang.reflect.Method method,Object[] args,MethodProxy methodProxy) throws Throwable。
设置了MethodInterceptor后，代理类的所有方法调用都会转而执行这个接口中的intercept方法而不是原方法。如果需要在intercept方法中执行原方法可以使用
参数method基于代理实例obj进行反射调用，但是使用方法代理methodProxy效率会更高（反射调用比正常的方法调用的速度慢很多）。MethodInterceptor的生成效率
不高，它的优势在于调用效率，它需要产生不同类型的字节码，并且需要生成一些运行时对象(InvocationHandler就不需要)。注意，在使用MethodProxy调用invokeSuper方
法相当于通过方法代理直接调用原类的对应方法，如果调用MethodProxy的invoke会进入死循环导致爆栈，原因跟InvocationHandler差不多。
*/
/*
public class MethodInterceptorDemo {
	public static void main(String[] args) throws Exception {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(SampleClass.class);
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
				System.out.println("Before invoking sayHello...");
				Object result = methodProxy.invokeSuper(obj, objects);
				System.out.println("After invoking sayHello...");
				return result;
			}
		});
		SampleClass sampleClass = (SampleClass) enhancer.create();
		System.out.println(sampleClass.sayHello("throwable"));
	}
}
*/
//输出结果：
//
//Before invoking sayHello...
//After invoking sayHello...
//throwable say hello!
//这个例子就是Spring的AOP中的环绕增强(Around Advice)的简化版，这里没有改变原来的方法的行为，只是在方法调用前和调用后织入额外的逻辑。

//【Dispatcher】
//Dispatcher，即分发器，提供一个方法Object loadObject() throws Exception;，同样地返回一个代理对象，这个对象同样可以代理原方法的调
//用。Dispatcher的loadObject()方法在每次发生对原方法的调用时都会被调用并返回一个代理对象来调用原方法。Dispatcher可以类比为Spring中的Prototype类型。
//
//public class DispatcherDemo {
//
//	private static final AtomicInteger COUNTER = new AtomicInteger(0);
//
//	public static void main(String[] args) throws Exception {
//		Enhancer enhancer = new Enhancer();
//		SampleInterfaceImpl impl = new SampleInterfaceImpl();
//		enhancer.setInterfaces(new Class[]{SampleInterface.class});
//		enhancer.setCallback(new Dispatcher() {
//			@Override
//			public Object loadObject() throws Exception {
//				COUNTER.incrementAndGet();
//				return impl;
//			}
//		});
//		SampleInterface sampleInterface = (SampleInterface) enhancer.create();
//		System.out.println(sampleInterface.sayHello("throwable-1"));
//		System.out.println(sampleInterface.sayHello("throwable-2"));
//		System.out.println(COUNTER.get());
//	}
//
//	private static class SampleInterfaceImpl implements SampleInterface{
//
//		public SampleInterfaceImpl(){
//			System.out.println("SampleInterfaceImpl init...");
//		}
//
//		@Override
//		public String sayHello(String name) {
//			return "Hello i am SampleInterfaceImpl!";
//		}
//	}
//}
//输出结果：
//
//SampleInterfaceImpl init...
//Hello i am SampleInterfaceImpl!
//Hello i am SampleInterfaceImpl!
//2
//计数器输出为2，印证了每次调用方法都会回调Dispatcher中的实例进行调用。
//
//【LazyLoader】
//LazyLoader，即懒加载器，它只提供了一个方法Object loadObject() throws Exception;，loadObject()方法会在第一次被代理类的方法调用时触发，它返回一个代
//理类的对象，这个对象会被存储起来然后负责所有被代理类方法的调用，就像它的名字说的那样，一种lazy加载模式。如果被代理类或者代理类的对象的创建比较麻烦，而且不确定它是
//否会被使用，那么可以选择使用这种lazy模式来延迟生成代理。LazyLoader可以类比为Spring中的Lazy模式的Singleton。
//
//public class LazyLoaderDemo {
//
//	private static final AtomicInteger COUNTER = new AtomicInteger(0);
//
//	public static void main(String[] args) throws Exception {
//		Enhancer enhancer = new Enhancer();
//		SampleInterfaceImpl impl = new SampleInterfaceImpl();
//		enhancer.setInterfaces(new Class[]{SampleInterface.class});
//		enhancer.setCallback(new LazyLoader() {
//			@Override
//			public Object loadObject() throws Exception {
//				COUNTER.incrementAndGet();
//				return impl;
//			}
//		});
//		SampleInterface sampleInterface = (SampleInterface) enhancer.create();
//		System.out.println(sampleInterface.sayHello("throwable-1"));
//		System.out.println(sampleInterface.sayHello("throwable-2"));
//		System.out.println(COUNTER.get());
//	}
//
//	private static class SampleInterfaceImpl implements SampleInterface{
//
//		public SampleInterfaceImpl(){
//			System.out.println("SampleInterfaceImpl init...");
//		}
//
//		@Override
//		public String sayHello(String name) {
//			return "Hello i am SampleInterfaceImpl!";
//		}
//	}
//}
//输出结果：
//
//SampleInterfaceImpl init...
//Hello i am SampleInterfaceImpl!
//Hello i am SampleInterfaceImpl!
//1
//计数器输出为1，印证了LazyLoader中的实例只回调了1次，这就是懒加载。
//
//【BeanCopier】
//JavaBean属性拷贝器，提供从一个JavaBean实例中拷贝属性到另一个JavaBean实例中，注意类型必须完全匹配属性才能拷贝成功(原始类型和其包装类不属于相同类型)。它还提
//供了一个net.sf.cglib.core.Converter转换器回调接口让使用者控制拷贝的过程。注意，BeanCopier内部使用了缓存和基于ASM动态生成BeanCopier的子类实现的转换
//方法中直接使用实例的Getter和Setter方法，拷贝速度极快(BeanCopier属性拷贝比直接的Setter、Getter稍慢，稍慢的原因在于首次需要动态生成BeanCopier的子类，
//一旦子类生成完成之后就和直接的Setter、Getter效率一致，但是效率远远高于其他使用反射的工具类库)。
//
//public class BeanCopierDemo {
//
//	private static final Map<String, BeanCopier> CACHE = new ConcurrentHashMap<>();
//
//	public static void main(String[] args) throws Exception {
//		//这里useConverter设置为false,调用copy方法的时候不能传入转换器实例
//		BeanCopier beanCopier;
//		String key = generateCacheKey(Person.class, Person.class);
//		if (CACHE.containsKey(key)) {
//			beanCopier = CACHE.get(key);
//		} else {
//			beanCopier = BeanCopier.create(Person.class, Person.class, false);
//			CACHE.put(key, beanCopier);
//		}
//		Person person = new Person();
//		person.setId(10086L);
//		person.setName("throwable");
//		person.setAge(25);
//		Person newPerson = new Person();
//		beanCopier.copy(person, newPerson, null); //这里转换器实例要传null
//		System.out.println(newPerson);
//	}
//
//	private static String generateCacheKey(Class<?> source, Class<?> target) {
//		return String.format("%s-%s", source.getName(), target.getName());
//	}
//
//	@ToString
//	@Data
//	private static class Person {
//
//		private Long id;
//		private String name;
//		private Integer age;
//	}
//}
//输出结果：
//
//BeanCopierDemo.Person(id=10086, name=throwable, age=25)
//在使用BeanCopier时候最好缓存BeanCopier实例，因为构造BeanCopier实例是一个耗时的操作。
//
//【ImmutableBean】
//ImmutableBean，即不可变的Bean。ImmutableBean允许创建一个原来对象的包装类，这个包装类是不可变的，任何改变底层对象的包装类操作都会
//抛出IllegalStateException。但是我们可以通过直接操作底层对象来改变包装类对象。这有点类似于Guava中的不可变视图或者JDK中的不可变集合。
//
//public class ImmutableBeanDemo {
//
//	public static void main(String[] args) throws Exception {
//		Person person = new Person();
//		person.setName("throwable");
//		Person immutablePerson = (Person) ImmutableBean.create(person);
//		System.out.println(immutablePerson.getName());
//		person.setName("doge");
//		System.out.println(immutablePerson.getName());
//		immutablePerson.setName("throwable-doge");
//		System.out.println(immutablePerson.getName());
//	}
//
//	@Data
//	private static class Person {
//
//		private String name;
//	}
//}
//输出结果:
//
//throwable
//doge
//Exception in thread "main" java.lang.IllegalStateException: Bean is immutable
//...

//【BeanGenerator】
//BeanGenerator，即Bean生成器，使用它能够在运行时动态的创建一个JavaBean。可以直接设置父类，生成的JavaBean就是父类类型的实例。
//
//public class BeanGeneratorDemo {
//
//	public static void main(String[] args) throws Exception {
//		BeanGenerator beanGenerator = new BeanGenerator();
//		beanGenerator.addProperty("name", String.class);
//		Object target = beanGenerator.create();
//		Method setter = target.getClass().getDeclaredMethod("setName", String.class);
//		Method getter = target.getClass().getDeclaredMethod("getName");
//		setter.invoke(target, "throwable");
//		System.out.println(getter.invoke(target));
//	}
//}
//输出结果：
//
//throwable

//【BulkBean】
//相比于BeanCopier，BulkBean创建时候依赖于确定的目标类型，Setter和Getter方法名称列表以及参数类型，它将copy的动作拆分为getPropertyValues()
//和setPropertyValues()两个方法，允许自定义处理属性。
//
//public class BulkBeanDemo {
//
//	public static void main(String[] args) throws Exception {
//		BulkBean bulkBean = BulkBean.create(
//				Person.class,
//				new String[]{"getName"},
//				new String[]{"setName"},
//				new Class[]{String.class});
//		Person person = new Person();
//		person.setName("throwable");
//		Object[] propertyValues = bulkBean.getPropertyValues(person);
//		System.out.println(Arrays.toString(propertyValues));
//		bulkBean.setPropertyValues(person, new Object[]{"doge"});
//		System.out.println(person.getName());
//	}
//
//	@Data
//	private static class Person {
//
//		private String name;
//	}
//}
//输出结果：
//
//[throwable]
//doge

//【BeanMap】
//BeanMap类实现了JDK的java.util.Map接口，将一个JavaBean对象中的所有属性转换为一个String-To-Obejct的Map实例。
//
//public class BeanMapDemo {
//
//	public static void main(String[] args) throws Exception{
//		Person person = new Person();
//		person.setName("throwable");
//		BeanMap beanMap = BeanMap.create(person);
//		System.out.println(beanMap);
//		System.out.println(beanMap.get("name"));
//	}
//
//	@Data
//	private static class Person {
//
//		private String name;
//	}
//}
//输出结果：
//
//{name=throwable}
//throwable

//【KeyFactory】
//KeyFactory源码中的注释是：Generates classes to handle multi-valued keys, for use in things such as Maps and Sets. Code for equals and hashCode methods follow the the rules laid out in Effective Java by Joshua Bloch.(翻译一下：通过生成类来处理多值键，以便在诸如Map和集合之类的东西中使用。equals和hashCode方法的代码遵循Joshua Bloch在《Effective Java》中列出的规则)。
//
//什么叫multi-valued keys?
//
//就是有多个键的组合，一起作为一个Key。
//
//比如[a b c]是一个组合，一起作为一个key，[2 3]也可以是作为一个key。
//
//KeyFactory就是用来生成这样一组Key的，通过两组的equals，hashCode等方法判断是否为同一组key的场景。为了描述Key的组合，需要定义一个接口，仅提供一个方法，叫
//做newInstance()，且返回值为Object，这个是使用KeyFactory的要求。
//
//public class KeyFactoryDemo {
//
//	public static void main(String[] args) throws Exception {
//		KeyFactoryInterface keyFactoryInterface1 = (KeyFactoryInterface) KeyFactory.create(KeyFactoryInterface.class);
//		KeyFactoryInterface keyFactoryInterface2 = (KeyFactoryInterface) KeyFactory.create(KeyFactoryInterface.class);
//		System.out.println(keyFactoryInterface1 == keyFactoryInterface2);
//		System.out.println(keyFactoryInterface1.equals(keyFactoryInterface2));
//		Object key1 = keyFactoryInterface1.newInstance(1, "doge");
//		Object key2 = keyFactoryInterface1.newInstance(1, "doge");
//		System.out.println(key1.equals(key2));
//		key2 = keyFactoryInterface1.newInstance(1, "doge10086");
//		System.out.println(key1.equals(key2));
//	}
//
//	interface KeyFactoryInterface {
//
//		Object newInstance(Integer a, String b);
//	}
//}
//输出结果：
//
//false
//true
//true
//false

//【Mixin】
//Mixin能够让我们将多个接口的多个实现合并到同一个接口的单个实现。
//
//public class MixinDemo {
//
//	interface InterfaceFirst {
//
//		String first();
//	}
//
//	interface InterfaceSecond {
//
//		String second();
//	}
//
//	static class ImplFirst implements InterfaceFirst {
//
//		@Override
//		public String first() {
//			return "I am first";
//		}
//	}
//
//	static class ImplSecond implements InterfaceSecond {
//
//		@Override
//		public String second() {
//			return "I am second";
//		}
//	}
//
//	interface MixinImpl extends InterfaceFirst, InterfaceSecond {
//
//	}
//
//	public static void main(String[] args) throws Exception {
//		Mixin mixin = Mixin.create(new Class[]{InterfaceFirst.class, InterfaceSecond.class, MixinImpl.class},
//				new Object[]{new ImplFirst(), new ImplSecond()});
//		MixinImpl mixinImpl = (MixinImpl) mixin;
//		System.out.println(mixinImpl.first());
//		System.out.println(mixinImpl.second());
//	}
//}
//输出结果：
//
//I am first
//I am second

//【StringSwitcher】
//用来模拟一个String到int类型的Map类型。如果在Java7以后的版本中，类似一个switch块的逻辑。
//
//public class StringSwitcherDemo {
//
//	public static void main(String[] args) throws Exception {
//		StringSwitcher stringSwitcher = StringSwitcher.create(new String[]{"one", "two"}, new int[]{1, 2}, true);
//		System.out.println(stringSwitcher.intValue("one"));
//		System.out.println(stringSwitcher.intValue("two"));
//	}
//}
//输出结果：
//
//1
//2

//【InterfaceMaker】
//接口生成器，底层依赖ASM的相关API。
//
//public class InterfaceMakerDemo {
//
//	public static void main(String[] args) throws Exception {
//		Signature signature = new Signature("foo", Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE});
//		InterfaceMaker interfaceMaker = new InterfaceMaker();
//		interfaceMaker.add(signature, new Type[0]);
//		Class<?> clazz = interfaceMaker.create();
//		Method[] methods = clazz.getMethods();
//		System.out.println(methods.length);
//		Method foo = methods[0];
//		System.out.println(foo.getReturnType());
//		System.out.println(Arrays.toString(foo.getParameterTypes()));
//	}
//}
//输出结果：
//
//1
//double
//[int]
//上述的InterfaceMaker创建的接口中只含有一个方法，签名为double foo(int)。InterfaceMaker与上面介绍的其他类不同，它依赖ASM中的Type类型。由于接口仅仅只用做在编译时期进行类型检查，因此在一个运行的应用中动态的创建接口没有什么作用。但是InterfaceMaker可以用来自动生成接口代码，为以后的开发做准备。
//
//【MethodDelegate】
//方法代理，个人认为作用不太大，这里仅举例。
//
//public class MethodDelegateDemo {
//
//	interface MethodDelegateInterface {
//
//		String getValueFromDelegate();
//	}
//
//	static class Delegate {
//
//		private String value;
//
//		public String getValue() {
//			return value;
//		}
//
//		public Delegate setValue(String value) {
//			this.value = value;
//			return this;
//		}
//	}
//
//	public static void main(String[] args) throws Exception {
//		Delegate delegate = new Delegate();
//		delegate.setValue("throwable");
//		MethodDelegate methodDelegate = MethodDelegate.create(delegate, "getValue", MethodDelegateInterface.class);
//		MethodDelegateInterface delegateInterface = (MethodDelegateInterface) methodDelegate;
//		System.out.println(delegateInterface.getValueFromDelegate());
//	}
//}
//输出结果：
//
//throwable

//【MulticastDelegate】
//多重代理，个人认为作用不太大，这里仅举例。
//
//public class MulticastDelegateDemo {
//
//	public interface DelegateProvider {
//
//		void setValue(String value);
//	}
//
//	static class MulticastBean implements DelegateProvider {
//
//		private String value;
//
//		@Override
//		public void setValue(String value) {
//			this.value = value;
//		}
//
//		public String getValue() {
//			return value;
//		}
//	}
//
//	public static void main(String[] args) throws Exception {
//		MulticastDelegate multicastDelegate = MulticastDelegate.create(DelegateProvider.class);
//		MulticastBean first = new MulticastBean();
//		MulticastBean second = new MulticastBean();
//		multicastDelegate = multicastDelegate.add(first);
//		multicastDelegate = multicastDelegate.add(second);
//		DelegateProvider provider = (DelegateProvider) multicastDelegate;
//		provider.setValue("throwable");
//		System.out.println(first.getValue());
//		System.out.println(second.getValue());
//	}
//}
//输出结果：
//
//throwable
//throwable

//【ConstructorDelegate】
//构造器代理，个人认为作用不太大，这里仅举例。
//
//public class ConstructorDelegateDemo {
//
//	public interface ConstructorInterface {
//
//		Object newInstance(String value);
//	}
//
//	static class ConstructorImpl {
//
//		private String value;
//
//		public ConstructorImpl(String value) {
//			this.value = value;
//		}
//
//		public String getValue() {
//			return value;
//		}
//
//		public ConstructorImpl setValue(String value) {
//			this.value = value;
//			return this;
//		}
//	}
//
//	public static void main(String[] args) throws Exception {
//		ConstructorInterface constructorInterface =
//				(ConstructorInterface) ConstructorDelegate.create(ConstructorImpl.class, ConstructorInterface.class);
//		ConstructorImpl constructorImpl = (ConstructorImpl) constructorInterface.newInstance("throwable");
//		System.out.println(ConstructorImpl.class.isAssignableFrom(constructorImpl.getClass()));
//		System.out.println(constructorImpl.getValue());
//	}
//}
//输出结果：
//
//true
//throwable

//【ParallelSorter】
//并行排序器，能够对多个数组同时进行排序，目前实现的算法有归并排序(mergeSort)和快速排序(quickSort)，查看源码的时候发现Float和Double类的比较直接用大于或
//者小于，有可能造成这两个类型的数据排序不准确(应该使用Float或Double的compare方法进行比较)。
//
//public class ParallelSorterDemo {
//
//	public static void main(String[] args) throws Exception {
//		Integer[][] array = new Integer[][]{
//				{4, 3, 9, 0},
//				{2, 1, 6, 0}
//		};
//		ParallelSorter.create(array).quickSort(0);
//		for (Integer[] row : array) {
//			System.out.println(Arrays.toString(row));
//		}
//	}
//}
//输出结果：
//
//[0, 3, 4, 9]
//[0, 1, 2, 6]

//【FastClass】
//FastClass就是对Class对象进行特定的处理，认知上可以理解为索引类，比如通过数组保存method引用，因此FastClass引出了一个index下标的新概念，比
//如getIndex(String name, Class[] parameterTypes)就是以前的获取method的方法。通过数组存储method，constructor等class信息，从而将原先
//的反射调用，转化为class.index的直接调用以提高效率，从而体现所谓的FastClass。
//
//public class FastClassDemo {
//
//	public static void main(String[] args) throws Exception {
//		FastClass fastClass = FastClass.create(SampleClass.class);
//		FastMethod fastMethod = fastClass.getMethod("sayHello", new Class[]{String.class});
//		SampleClass sampleClass = new SampleClass();
//		System.out.println(fastMethod.invoke(sampleClass, new Object[]{"throwable"}));
//		System.out.println(fastMethod.getIndex());
//	}
//}
//输出结果：
//
//throwable say hello!
//0
//实际上，在接口或者代理类的方法比较少的时候，使用FastClass进行方法调用有可能比原生反射方法调用Method#invoke()高，但是实际还是需要进行测试和分析，不能盲目一概而论。
//
//小结
//本文简单分析了一下CGLIB中的常用API，其实在实现AOP、动态代理和反射调用的时候，最常用的是字节码增强器Enhancer、回调(Callback)以及快类(FastClass)，掌握它们的使用方式有利于进行AOP编程以及反射性能创新性提升。

/*
【MethodInterceptor-方法拦截器】
MethodInterceptor 翻译过来就是方法拦截器，它基础于Callback，说明它也属于回调。
我们先要创建一个 代理类方法拦截器，实现net.sf.cglib.proxy.MethodInterceptor接口，用作代理类的方法拦截处理，它的用法其实和JDK动态代理
的java.lang.reflect.InvocationHandler一样。
其中MethodInterceptor接口中只有一个方法intercept:
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
它接受4个参数:
obj – 代理的原类对象
方法 – 拦截实现的方法
args – 方法中的参数组
proxy – 用于调用 父类代理； 可以根据需要多次调用，既做到多次代理。
这里面的proxy对象是MethodProxy类，不同于JDK动态代理InvocationHandler中的proxy，它拥有一个invokeSuper方法，能调用原始（父类）的方法，类似于Method类
的Invoke方法调用。
所以代理类方法拦截器例子如下：

public class ProxyInterceptor implements MethodInterceptor {

//     * 当对基于代理的方法回调时，拦截其方法，进行自定义处理
//     * @param obj 代理对象
//     * @param method 拦截的方法
//     * @param args 拦截的方法的参数
//     * @param proxy 代理
//     * @return
//     * @throws Throwable
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object result = null;
        System.out.println("method invoke before...");
        result = proxy.invokeSuper(obj, args);
        System.out.println("method invoke after...");
        return result;
    }
}
上面除了在调用方法代理使用了MethodProxy的invokeSuper方法之外，其余的基本和JDK动态代理的InvocationHandler的实现类编写方法一样。
Enhancer代理
有了代理类方法拦截器 后，我们就可利用net.sf.cglib.proxy.Enhancer类来创建动态代理了。

其中Enhancer类概念和原本JDK动态代理的Proxy类几乎一样，创建Enhancer类来进行代理类，但和Proxy不同的是，Enhancer既能够代理普通的class，也能够代理接口。

Enhancer创建一个被代理对象的子类并且拦截所有的方法调用（包括从Object中继承的toString和hashCode方法）。注意：Enhancer不能够拦截final方法

public class ProxyMain {
    public static void main(String[] args) {
        // 创建Enhancer类,用作实现Student类的代理
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback(new ProxyInterceptor());

        // 创建Enhancer代理,来代理Student类
        Student o = (Student) enhancer.create();
    }
}
其中创建Enhancer后需要设置两个参数，一个为Superclass：代理的类，一个为Callback：回调对象。

最后需要调用其对象的create方法，即建立一个代理类出来。其代理类的所有非final参数、方法都会被其拦截，返回至MethodInterceptor中进行自定义处理。

当然直接使用Enhancer中的静态Create方法，输入两个参数也可以进行快速创建一个代理类出来。

Student o = (Student) Enhancer.create(Student.class, new ProxyInterceptor(new Student()));
当我们直接打印一下对象，看看会出现什么？

结果如下：

method invoke before...
method invoke before...
method invoke after...
method invoke after...
com.test2.bean.Student$$EnhancerByCGLIB$$72d6b74c@2957fcb0
控制台产生两次代理拦截处理，而且为什么为什么还会有EnhancerByCGLIB字段，这时大伙可能会产生疑惑。

其实都知道直接打印对象的话，其实调用的是对象的toString方法，而在默认的toString方法中：

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
其中getName()也会调用一次内部方法，所以自然会产生两次拦截。而不同的是getClass()默认是属于final类型的，前面说了Enhancer的特性，所以自然不能被拦截，自然会暴露出原Enhancer类型出来。

当然，上面的操作需要先创建代理方法拦截类还要在运行下创建Enhancer代理，对于工程中必然显得代码重复了，所以可以直接精简为一个工具类：

 * Enhancer代理生成工具
 * @version 1.0
 * @since JDK1.7
public class ProxyUtil implements MethodInterceptor {

    //代理的类对象
    private Object obj;
    
     * 创建代理类
     * @param target 代理的类对象
     * @return
    public Object createProxy(Object target) {
        this.obj = target;
        Enhancer enhancer = new Enhancer();
        //设置代理目标
        enhancer.setSuperclass(this.obj.getClass());
        //设置单一回调对象，在调用中拦截对目标方法的调用
        enhancer.setCallback(this);
        //设置类加载器
        enhancer.setClassLoader(this.obj.getClass().getClassLoader());
        
        return enhancer.create();
    }
     * 方法描述 当对基于代理的方法回调时，在调用原方法之前会调用该方法
     * 拦截对目标方法的调用
     *
     * @param obj 代理对象
     * @param method 拦截的方法
     * @param args 拦截的方法的参数
     * @param proxy 代理
     * @return
     * @throws Throwable
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        Object result = null;
        
        result = proxy.invokeSuper(obj, args);
        
        return result;
    }

}
使用:

ProxyUtil proxyUtil = new ProxyUtil();
XXX xxx = (XXX)proxyUtil.createProxy(new XXX());
CallbackFilter-回调过滤器
有些时候我们可能只想对特定的方法进行拦截，对其他的方法直接放行，不做任何操作。这种情况下我们这需要在Enhancer上绑定一个CallbackFilter。

CallbackFilter是一个net.sf.cglib.proxy下的一个接口，该接口主要作用允许您在代理的方法中控制回调规则，起着过滤作用。

其CallbackFilter接口中含有两个方法，其中accept方法用于将其代理方法进行过滤然后分别回调。

例如，我们实现一个CallbackFilter接口类：

     //* PersistenceService代理类方法回调过滤器
    public class PersistenceServiceCallbackFilter implements CallbackFilter {

        //SAVE方法 回调组的序号
        private static final int SAVE = 0;

        //LOAD方法 回调组的回调序号
        private static final int LOAD = 1;

         * 代理方法过滤，控制回调
         * @method 代理类的方法
         * @return 回调组的序号
        public int accept(Method method) {
            String name = method.getName();
            if ("save".equals(name)) {
                return SAVE;
            }
            // 对于其他方法
            return LOAD;
        }
    }
上面是一个简单的自定义回调过滤器，它执行了过滤操作，将其 save方法进行返回 0 ，其余的方法返回 1。

回调过滤器的Accept 方法将代理方法映射到回调。返回值是特定方法的回调数组中的索引。

随后在其Enhancer绑定回调过滤器：

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersistenceServiceImpl.class);

CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();
// 绑定回调过滤器
enhancer.setCallbackFilter(callbackFilter);

AuthorizationService authorizationService = ...
Callback saveCallback = new AuthorizationInterceptor(authorizationService);
Callback loadCallback = NoOp.INSTANCE;
// 设置回调数组
Callback[] callbacks = new Callback[]{saveCallback, loadCallback };
enhancer.setCallbacks(callbacks);
...
return (PersistenceServiceImpl)enhancer.create();
enhancer.setCallbacks方法设置了回调数组，这个回调数组的索引就是这个上面Accept 方法返回索引对应。

NoOp的回调行为 表示返回原类方法执行，即放行。

所以当代理类执行save方法后，就会被回调到AuthorizationInterceptor上。如果执行其他方法，那么就会直接放行执行。

CallbackHelper
使用CallbackFilter进行方法回调过滤，那么Enhancer就还需要自己手动设置回调数组，如果回调有一两个还好，如果有很多需要进行过滤的话，那么不但不好设置回调数组，还容易搞混索引。

能否只写过滤规则直接跳转到XX回调，不写回调数组呢？

可以！CGLIB提供了一个快速过滤类 - CallbackHelper。

net.sf.cglib.proxy.CallbackHelper类实现了CallbackFilter接口，它在创建后自动将其回调数组进行排序，用户使用它只需要关注过滤规则和回调，不需要关注回调数组的排序索引。

CallbackHelper的构造方法需要两个参数:

Class superclass：代理的类

Class[] interfaces：代理的类的实现的接口组，如果没有的话，可以传入new Class[0]

CallbackHelper需要重写它的getCallback(Method method)方法，当代理类每次执行一个方法时，其代理方法都会通过getCallback这个方法中，在其进行过滤设置回调。

所以将其过滤条件放置在getCallback方法内：

CallbackHelper helper = new CallbackHelper(Student.class, new Class[0]) {
            @Override
            protected Object getCallback(Method method) {
               // 如果方法名为sout的话,将其方法返回至ProxyInterceptor处理
                if (method.getName() == "sout") {
                    return new ProxyInterceptor();
                } else {
                    // 否则直接将其方法回调给默认实现,即正常执行
                    return NoOp.INSTANCE;
                }
            }
        };
// 创建Enhancer代理,来代理Student类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Student.class);
// 设置要使用的代理方法回调数组
enhancer.setCallbacks(helper.getCallbacks());
// 设置要代理方法回调过滤器
enhancer.setCallbackFilter(helper);
Student o = (Student)enhancer.create();
Enhancer在设置回调数组时，只需使用CallbackHelper.getCallbacks()即可获取对应的回调数组。所以使用CallbackHelper会比一般的自定义CallbackFilter使用简便。

更多回调行为
上面我们只介绍了两种回调行为 MethodInterceptor方法拦截 和 NoOp放行，CGLIB还提供了很多其他回调行为接口：

net.sf.cglib.proxy.FixedValue : 直接强行返回指定内容，在一些场景下可以提升处理性能。例如：
Enhancer enhancer = new Enhancer();
//设置代理目标
enhancer.setSuperclass(hello.getClass());
//设置单一回调对象，在调用中拦截对目标方法的调用
enhancer.setCallback(new FixedValue() {
    @Override
    public Object loadObject() throws Exception {
        // TODO Auto-generated method stub
        return "FixedValue";
    }
});
Object obj = enhancer.create();
上述行为，不管代理类其调用方法内容如何，都会返回“FixedValue”这个字节串，并且调用方法内其他不执行。

net.sf.cglib.proxy.LazyLoader：当实际的对象需要延迟装载时，可以使用LazyLoader回调。一旦实际对象被装载，它将被每一个调用代理对象的方法使用;
net.sf.cglib.proxy.Dispatcher：Dispathcer回调和LazyLoader回调有相同的特点，不同的是，当代理方法被调用时，装载对象的方法也总要被调用;
net.sf.cglib.proxy.ProxyRefDispatcher：ProxyRefDispatcher回调和Dispatcher一样，不同的是，它可以把代理对象作为装载对象方法的一个参数传递;
Bean操作
CGLIB除了可以用Enhancer来实现动态代理外，其net.sf.cglib.beans包中还可对其JAVA Bean进行操作，配合JAVA-Reflection（反射机制）同样可以做到动态制作类、创建类。

创建Bean
使用net.sf.cglib.beans.BeanGenerator类可以进行创建Bean对象操作。

BeanGenerator beanGenerator = new BeanGenerator();
beanGenerator.addProperty("name",String.class);
Object o1 = beanGenerator.create();

Method m1 = o1.getClass().getMethod("setName",String.class);
m1.invoke(o1,"zxx");

Method m2 = o1.getClass().getMethod("getName");
System.out.println(m2.invoke(o1));
使用CGLIB的BeanGenerator动态的创建了一个Bean对象，使用addProperty方法可以添加一个属性，在添加属性的同时BeanGenerator会自动生成其Getting、Setting方法。

使用其BeanGenerator的create的方法进行Bean实例化，配合Java反射可以进行对Bean的操作。

创建只读 Bean
使用net.sf.cglib.beans.ImmutableBean类可以进行根据现有的Bean对象，创建一个对应的只读Bean对象。

Student student = new Student();
student.setName("zzz");
// 创建一个student实例的对应只读Bean
Student o = (Student) ImmutableBean.create(student);
student.setName("ooo");
System.out.println(o.getName()); //只读Bean对象的值会自动改变

o.setName("kkk"); // 抛出IllegalStateException,不可直接修改只读Bean
ImmutableBean的对象虽然可以强制为对应设置的Bean类型，但是无法直接进行修改设置，其属性随原Bean改变而改变。

利用BeanMap实现对象与Map的转换
利用net.sf.cglib.beans.BeanMap类可实现将Bean对象转换为Map对象，其中Bean对象中的属性全部以Key-Value方式放置在Map中。

BeanGenerator beanGenerator = new BeanGenerator();
beanGenerator.addProperty("name",String.class);
beanGenerator.addProperty("age",Integer.class);
Object o1 = beanGenerator.create();

Method m1 = o1.getClass().getMethod("setName",String.class);
m1.invoke(o1,"zxx");
Method m2 = o1.getClass().getMethod("setAge",Integer.class);
m2.invoke(o1,23);
// 为o1对象创建BeanMap
BeanMap beanMap = BeanMap.create(o1);
Integer age = (Integer) beanMap.get("age");
BeanMap基于Map类型，拥有Map的存储功能，自然也可以用containsKey、containsValue、keySet等功能。

在Java中bean与map的转换有很多种方式，比如通过ObjectMapper先将bean转换为json，再将json转换为map 或者 通过Java反射等，但是这些方法都没有使用CGLIB中的BeanMap来的快，因为这种方式效率极高，它跟第其它方式的区别就是因为它使用了缓存，所以优先考虑使用BeanMap。

利用Mixin实现多个对象整合为单个对象
Mixin能够将多个Bean对象整合为一个Bean对象，这个Bean对象拥有它们所有方法。

但前提就是这些Bean对象必须是实现接口的，Mixin通过接口来对对象进行整合的。

interface Interface1 {
    String one();
}

interface Interface2 {
    String two();
}

interface MixinInterface extends Interface1, Interface2 {
    String one(String ok);
    String three();
}

static class Class1 implements Interface1 {
    @Override
    public String one() {
        return "one";
    }
}

static class Class2 implements MixinInterface {
    @Override
    public String three() {
        return "three";
    }

    @Override
    public String one() {
        return "no 22one";
    }

    @Override
    public String one(String ok) {
        return "no one";
    }

    @Override
    public String two() {
        return "two";
    }
    public static void main(String[] args) {
        Mixin mixin = Mixin.create(new Class[]{Interface1.class,MixinInterface.class},
                new Object[]{new Class1(), new Class2()});
        MixinInterface mixinInterface = (MixinInterface) mixin;
        System.out.println(mixinInterface.two());
    }
}

Mixin.create方法接受两个参数：第一为接口类组，第二为对象组。其中接口组与对象组一一对应，引索对应。

其实Mixin这个工具几乎不怎么被使用，因为它依据与接口进行创建，很多时候可以通过纯Java的方式实现，没有必要使用Minix类。

类操作
CGLIB可以进行对类进行操作，获取到类中的各个属性、方法代理、接口创建等功能，在某些方面上比JDK的反射更强大。

利用InterfaceMaker动态创建接口
利用net.sf.cglib.proxy.InterfaceMaker可以动态创建Interface接口，并且可以自定义接口内容。

// 创建一个Signature方法认证类
Signature signature = new Signature("hello", Type.INT_TYPE, new Type[]{Type.getType(String.class)});
InterfaceMaker maker = new InterfaceMaker();

maker.add(signature,new Type[0]);
Class aClass = maker.create();
上述的InterfaceMaker创建的接口中含有了一个方法，为Integer hello(String XXX)。

InterfaceMaker类的add方法用于向自定义接口添加方法，它有很多选择，它可以直接接受一个Method类等。

上面我们使用了传入Signature类，它的定义起来更简单方便，它还需要一个抛出的exceptions数组，用于方法存在的exceptions抛出声明，当然这儿可以直接创建一个空数组，来表示这个方法没有异常抛出。

对于Signature方法认证类，它实际是一个Method的简单描述类：（这个类是net.sf.cglib.core.Signature,不是SpringAOP中的Signature）

Signature的有两个构造方法：

* @param name 方法名
* @param desc 简介
public Signature(String name, String desc)

* @param name 方法名
* @param returnType 返回类型数组
* @param argumentTypes 参数类型数组
public Signature(String name, Type returnType, Type[] argumentTypes)
其中我们常用第二个，它直接提供了返回类型和参数类型的选择，在创建方法时这些都可以根据自己的类型来进行调用。

由于接口仅仅只用做在编译时期进行类型检查，因此在一个运行的应用中动态的创建接口其实没有什么用。

但是InterfaceMaker可以用来自动生成代码，为以后的开发做准备。

利用MethodDelegate对方法进行代理
CGLIB可以通过一个只含有一个方法的接口 使用MethodDelegate来 代理对象中某一个方法，最终让该接口的那个方法来代理对象中指定的方法。

interface ProxyMethod{
    String getValueFromProxy();
}

...
BeanGenerator beanGenerator = new BeanGenerator();
// 创建一个Bean 内部拥有 value属性
beanGenerator.addProperty("value",String.class);
Object o = beanGenerator.create();
Method method = o.getClass().getMethod("setValue", String.class);
method.invoke(o,"zty");

// 将其ProxyMethod接口的方法代理 o对象中getValue方法
ProxyMethod delegate = (ProxyMethod)MethodDelegate.create(o, "getValue", ProxyMethod.class);
System.out.println(delegate.getValueFromProxy());  // 结果为 "zty"
Method.create方法接受3个参数:

第二个参数为即将被代理的方法

第一个参数必须是一个无参数构造的bean。因此MethodDelegate.create并不是你想象的那么有用

第三个参数为只含有一个方法的接口。当这个接口中的方法被调用的时候，将会调用第一个参数所指向bean的第二个参数方法

MethodDelegate虽然用接口可以代理方法,但是拥有很多缺点:

为每一个代理类创建了一个新的类，这样可能会占用大量的永久代堆内存
你不能代理需要参数的方法
如果你定义的接口中的方法需要参数，那么代理将不会工作，并且也不会抛出异常；如果你的接口中方法需要其他的返回类型，那么将抛出IllegalArgumentException
所以大部分时间不建议使用MethodDelegate代理方法。

FastClass - 另一种Class类
CGLIB中的FastClass类是比JDK中Class类更好（它们声称的）。相比于传统的Class类，FastClass引出了一个index下标的新概念。

通过create方法创建一个对应的FastClass类，它通过数组存储类中的所有method,constructor等class信息，用户通过向数组下标index寻找对应的方法或者构造器等，用方法签名转化为对应index方法，用模板方式解决Java语法不支持问题，同时改善Java反射性能。

传统的Class类使用Java反射机制来确定方法等，而FastClass则是通过使用index快速确定对应的方法等，从而达到高效率。

FastClass fastClass = FastClass.create(Student.class);
FastMethod fastMethod = fastClass.getMethod("getName",new Class[0]);

Student student = new Student();
student.setName("zty");
System.out.println(fastMethod.invoke(student,new Object[0]));  // 输出"zty"
FastClass的使用反射和普通的Class类似，获取到的FastMethod方法、FastConstructor构造方法，需要参数也相同。

注意：FastMethod中的invoke方法是调用的FastClass中的invoke方法，它也没有无参的方法，所以当参的情况下，需要传入一个空的对象数组。

FastClass的实现逻辑，是生成增强类实现invoke方法，invoke方法中，用switch语义将被增强类的所有方法调用枚举出来。用户使用FastClass.invoke方法，传入方法签名和被调用实例，从而达到不使用反射就能实现不确定方法的调用。

但是！FastClass 这一工具在提出来的时候Java反射并没有进行足够的优化，所以或许FastClass会更效率，但是从JDK7开始到目前的Java新版本的JVM拥有inflation这个概率， 当一个反射方法调用次数少于15次时，会使用调用native方法。大于15次之后则使用ASM生成新的类类处理反射调用。所以在JDK7后面的版本使用FastClass反而比Class慢，所以不推荐使用！**

使用警告
使用所有的 CGLIB类进行操作都会生成字节代码，这会导致额外的类被存储在 JVM 内存的一个特殊区域，如果操作过多，容易造成永久堆满，触发OutOfMemory异常。

或许这样说来，大伙可能会放弃使用CGLIB，但是，如果您明智而谨慎地使用 cglib，您可以用它做一些令人惊奇的事情，利大于弊，因此，节制地使用 CGLIB进行操作。
*/